home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Belgian Amiga Club - ADF Collection
/
BS1 part 19.zip
/
BS1 part 19
/
Lattice C disk 5.adf
/
addendum.doc
next >
Wrap
Text File
|
1988-11-07
|
60KB
|
1,479 lines
This file contains omissions/corrections which will be incorporated
into the version 5.0 documentation at a later date.
===================================================================
TABLE OF CONTENTS
(1) Assembly Language Topics
-- Version 5.0 Enhancements
-- Writing Assembly Language Modules for C
-- The CSECT Directive
-- Relative Addressing
(2) Linker Topics
-- New Features of Blink
-- Introduction to Overlays
-- Custom Overlay Managers
(3) Creating Load-and-Stay Resident Programs
(4) Compiler-Defined Preprocessor Symbols
(5) Direct Calling of Amiga Libraries
-- Coding Library Calls for In-Line Code Generation
(6) Using the FD2PRAGMA Program
(7) Built-In Functions
(8) Header File Compression
-- Header File Organization
(9) Library Documentation Corrections
(10) Additional Notes On The Source Level Debugger
(11) New Keywords in 5.0
(12) Library Descriptions
(13) Description of Binary Patch Program
(14) 68881 Notes
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::: 1 ::::::::::::: ASSEMBLY LANGUAGE TOPICS :::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The assembler included in this package has many new features.
Addressing Modes.
Listed below are all the addressing modes supported.
Mode Example Notes
Dn add.w d1,d0
An addq.w #1,a1
(An) add.w (a1),d0
(An)+ add.w (a1)+,d0
-(An) add.w -(a1),d0
d16(An) add.w 10(a1),d0
d8(An,Xn) add.w 10(a1,a2.l),d0
bd(An,Xn) add.w $10000(a1,a2.l),d0 68020 only
([bd,An],Xn,od) add.w ([10,a1],a2.l,20),d0 68020 only
([bd,An,Xn],od) add.w ([10,a1,a2.l],20),d0 68020 only
(xxx).W add.w (100).w,d0
(xxx).L add.l (100).l,d0
#<data> add.l #100,d0
d16(PC) add.w 10(PC),d0
d8(PC,Xn) add.w 10(PC,a2.l),d0
bd(PC,Xn) add.w $10000(PC,a2.l) 68020 only
([bd,PC],Xn,od) add.w ([10,PC],a2.l,20),d0 68020 only
([bd,PC,Xn],od) add.w ([10,PC,a2.l],20),d0 68020 only
where
d8 is an eight bit number
d16 is a sixteen bit number
bd is a 32 bit byte displacement
od is the 32 bit outer displacement
An is an address register (a0-a7)
Dn is a data register (d0-d7)
Xn is an index register (a0.w-a7.l)
Note:
All the operands on the addressing modes marked 68020 are
optional.
Data Formats.
New data formats are allowed for 68881 floating point instruction.
These include:
#2.1
#2.1E+10
These will be converted into the proper floating point formats
according to the type of instruction. For example, in the following
instruction:
fmove.s #2.1,fp1
2.1 will be in single precsion.
Other sizes allowed are:
fmove.d #2.1,fp1 double precision
fmove.x #2.1,fp1 extended precision
Note that the packed data format is not converted for you.
Also if you want to specify the bit pattern by hand you can use the
following formats:
fmove.d #$12345678,fp1 32 bit pattern
fmove.s #$123456781234568,fp1 64 bit pattern
fmove.x #$123456781234567812345678,fp1 96 bit pattern
You can also specify the constants in octal (ie @123456712) or
binary (ie %0110110100110101).
Opcodes.
All the 68020 opcodes have been added. A new size for all the branch
instructions has been added. The size fields allowed on branches are
demostrated below.
BSR.S or BSR.B generate 8 bit offsets
BSR.W generates 16 bit offsets
BSR.L generates 32 bit offsets (Note that
this is only supported on the 68020
and that BSR.L to externs are not
allowed.)
BSR with no size will calculate the size
needed, 16 bit for externs.
*NOTE that this is different from the way the previous version of
the assembler worked. For example, the old assembler would
make the instruction
BRA.L FOO
a 16 bit branch. This version makes a 32 bit branch which is
only supported on the 68020.
___________________________________________________________________
-------------------------------------------------------------------
WRITING ASSEMBLY LANGUAGE MODULES FOR C
You can write assembly language modules for inclusion in C programs
if you are sufficiently experienced with the 68000 instruction set
and if you observe the following:
1. The statements defining the functions should be placed in the
text section by preceding them with:
CSECT text
This is not an absolute requirement, since the functions will be
accessible regardless of the section in which they are defined,
but it assures them of placement with the C functions during
linking (if the -s option is used). Each function entry must be
declared in an XDEF statement:
XDEF AFUNC
.
.
.
FUNC EQU *
2. If the module is to define data locations to be accessed (using
extern declarations) in C modules, those definitions should be
placed in the data section by preceding them with:
CSECT data
Each data element must be declared in an XDEF statement in order
to be accessible in the C modules:
XDEF D0,D1,D2
D0 DC.L $4000
D1 DC.W $8000
D2 DC.L D0
3. Any of the registers D2-D7 or A2-A6 must be preserved by the
module, and the return value loaded into the appropriate data
registers.
To call a C function from an assembly language module, you must
include an XREF declaration for the function. Before calling the
function (via JSR), you must supply any expected arguments in the
proper order. After control returns from the called function, the
stack pointer must be adjusted to account for pushed arguments.
XREF cfunc
MOVE.L DO,-(A7) ;push argument
MOVE.L D1,-(A7)
JSR cfunc ;call function
ADDQ #8,A7 ;restore stack ptr
Data elements defined in a C module may be accessed via XREF
statements, as well:
XREF XD2,XD3
.
.
.
MOVE.L XD2,D0
___________________________________________________________________
-------------------------------------------------------------------
THE CSECT DIRECTIVE
All of the standard 68000 instructions are supported, as described
in the Motorola manual. Assembly directives are discussed in the
following paragraphs.
Instead of the section directive, the Lattice assembler uses the
CSECT directive of the form:
CSECT name
where name is the control section name. Note that a CSECT directive
must precede any data-defining statements (i.e., there is no default
control section). The Lattice 68000 macro assembler uses the concept
of named control section to control the location of data and text
during linking; the CSECT directive defines the start of a
relocatable control section of the specified name. Note that "text"
and "data" are used for compatibility with object files generated by
the C compiler. Only one instance of a CSECT directive with a
particular name is permitted; that is, scattered definitions of data
for a CSECT are not allowed.
The CSECT directive may be used to specify additional information
about the control section; its general format is:
CSECT name,type,align,rtype,rsize
Only the name parameter must be present; it specifies the name of
the control section. type is zero for code sections, one for data
sections, and 2 for uninitialized data sections (like the udata
section used by the C compiler); the default value is zero (code).
align specifies the alignment requirements of the control section
as a power of two; thus, align equal to one means the section will
begin on an even address, and a value of two means an address
divisible by 4, etc. This parameter is non-functional on the Amiga.
All sections are long-word aligned.
The last two parameters specify the type and size of relocation
information associated with the address of any data in the control
section. rtype specifies the relocation type, which determines the
meaning of the address, or relocation data; the default value is
zero.
rsize specifies the size, in bytes, of the relocation data for the
section; the default value is four. The legal types and sizes of
relocation information on the 68000 are summarized in the following
table:
Type Size (bytes) Description
0 2 or 4 Absolute short or long address
1 2 PC-relative offset
2 2 Address-register-relative offset
Type 0 relocation results in an absolute address, while type 1
relocation is computed as the difference between the referenced
address and the location of the reference. Type 2 relocation is
used to assign offsets for address-register-relative addressing of
data. Both the base section used for such addressing and any bias
placed in the address register are used when a load module is
constructed.
Note that symbols declared in XREF statements within a control
section will be addressed using the relocation type and size
defined for that section. The use of CSECT directives which are
compatible with the -b and -r options of the C compiler are
described later.
___________________________________________________________________
-------------------------------------------------------------------
RELATIVE ADDRESSING
The Lattice assembler can be used to generate assembly language
modules which are compatible with the conventions used in C programs
compiled with the -b option on lc1 or the -r option on lc2. PC-
relative external function calls can be forced using sequences such
as the following:
CSECT TEXT,0,,1,2
XREF cfunc
XDEF afunc
afunc JSR cfunc(PC)
RTS
This mechanism is the same as that used for function calls in code
compiled with the -r option on lc2. Note that the XREF statement
for the external function being called must appear after the CSECT
statement, and that all calls must be made in the PC-relative
addressing mode. If the XREF statement for the external function
precedes the CSECT statement a standard JSR to a four-byte external
address can be used.
Address-register-relative addressing of data can be forced if the
data is placed in a control section such as the following:
CSECT data,1,,2,2
XREF xdata
data1 DC.W 10
data2 DC.L 20
It then must be addressed using the appropriate address register:
MOVE.W data1(A5),DO
MOVE.L data2(A5),D1
MOVE.L d1,xdata(A5)
this mechanism is the same as that used for addressing data in
code compiled with the -b option on lc1. Note that the XREF
statement for any external data elements being addressed must appear
after the CSECT statement, and that all data references must be made
in the address-register-indirect-with-displacement addressing mode.
If the XREF statement for the external data precedes the CSECT
statement, a standard reference to a four-byte external address can
be used.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::: 2 ::::::::::::::::::: LINKER TOPICS ::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
NEW FEATURES OF BLINK
1. Strip Debugging Information with Blink
Blink will allow the stripping of debugging information given an
executable that contains debugging info. To do this type:
blink oldfile TO newfile NODEBUG
where oldfile is an executable file that contains debugging
information, and newfile is a new executable file that will be
stripped of debugging information.
2. Generate Programs that can be made Resident
To generate an executable file that can be made resident simply
link with the startup file CRES.O, CATCHRES.O, or CATCHRESNR.O
instead of the normal startup file C.O. The executable file
will have the PURE bit set. There are some rules that must be
followed when doing this.
-- Never use -b0 when compiling the file.
-- All data hunks must be mergeable.
-- There should be no absolute references to data.
-- Use #pragmas instead of AMIGA.LIB when possible.
In some cases it may be necessary to link with AMIGA.LIB. This is
ok as long as BLINK doesn't generate any warning about references to
absolute symbols.
The following diagram describes how resident programs run.
An old executable looks When running resident
like this: programs:
|--------------------| |---------------------|
| | | |
| CODE | | CODE |
| | | |
|--------------------| |---------------------|
| | | |
| MERGED DATA | | MERGED DATA |
| | | (used only during |
|--------------------| | load ) |
| MERGED BSS | |---------------------|
|--------------------| .
.
.
|----------------------|
| Segmented Allocated |
| by cres.o and used |
| by running program |
| Referenced off of A4 |
| |
|----------------------|
Each time the program is run a new data segment is created, old
data is copied into it, and relocations are performed inside it.
When the program terminates, the segment is released.
The special symbols that blink creates are as follows:
_LinkerDB pointer to data section
RESOFF offset that LinkerDB is in data (0 or 0x8000)
RESLEN number of longwords in data
NEWDATAL number of bytes in new data segment including BSS
The number of relocs in the data segment is stored just after the data
segment, with the relocs following this.
___________________________________________________________________
-------------------------------------------------------------------
INTRODUCTION TO OVERLAYS
Overlays are code groups and data hunk groups which reside in main
memory only while in use. When one loaded group is no longer needed
and another is needed, the memory used by the old group is reclaimed,
and may be used to load in the new group. This group loading and
unloading is performed automatically.
Overlays on the Amiga are organized as a tree structure with nodes.
Each node consists of a group of code hunks and data hunks. The tree
consists of a ROOT NODE, which is permanently resident, and one or
more OVERLAY NODES. The root node usually contains the libraries,
your main program, and extensively used subroutines, etc. The
overlay nodes consist of hunks which are to be loaded only as needed
to conserve memory space.
Figure 1: Overlay Tree
Root
|
-------------------
overlay1 overlay2
|
--------------------
overlay3 overlay4
|
overlay5
In the overlay tree in Figure 1, overlay nodes 1 and 2 execute
independently, using the same memory, one at a time. Also, if
overlay node 2 is loaded, it can then load either of nodes 3 and 4,
which share a block of memory in which they can execute. Thus, if
3 is loaded and executing, 4 and 1 are NOT loaded, overlay node
2 is loaded, and overlay 5 may be loaded. If overlay node 1
is loaded and executing, then nodes 2 through 5 are NOT currently in
memory.
Your overlay tree need not be a binary tree, balanced, or of a depth
greater than 1. You can have 50 overlay nodes, all hanging from the
root node. The nodes may be all mutually exclusive and independent
of each other, or one long chain of overlay nodes. You must decide
which of your routines can operate independently of each other.
Overlays are not a general-purpose mechanism. They are intended
primarily for large applications (100k or more), which must run in a
small amount of memory in order to leave more room for data. Such
applications should also have a great deal of modularity, with a
definite calling hierarchy among the code routines.
One good example (except for size) is a linker. Although it does
not use overlays, it is feasible for blink to have overlays. If you
run blink with the VERBOSE option, you see messages about
"Parsing, Pass1," and so on. These represent clear phases of linking
that are independent of each other--the only continuity is in the
internal data structures they create and manipulate.
To maximize available memory, either for the linker's data structures
or for other concurrent programs and their data, blink could have
been linked to use overlays, allowing no more than about 5k to 10k
resident in memory at any given moment. The sole overhead would be a
slight drop in speed performance due to the individual loading of the
separate overlay nodes. Fortunately, since blink is relatively small
and efficient in memory usage, it does not use overlays.
1. The Overlay Manager
Overlays depend on the overlay manager. Every time a routine in
an overlay node is referenced, a call is made to the Overlay
Manager, which then scatter loads the desired overlay node,
unloading any existing nodes which exist on another, mutually
exclusive branch of the tree. These calls to the Overlay Manager
are done by blink automatically and are effectively invisible to
your own code.
The Overlay Manager can be put into the libraries rather than
kept as a separate file, as under ALINK. You can also override
the original Overlay Manager with one of your own, thanks to an
extension in blink. (See Custom Overlay Managers later in this
text for more information on this.)
2. Using Overlays
When using overlays, you should design your overlay tree after
you have an idea of the calling hierarchy of your application.
Those routines which are called frequently, or which are called
by almost every other routine in your application, should be
placed in the root node. Generally, the more commonly accessed
the routine, the closer to the root node it should be. Major
independent routines however, should be placed in nodes which
are more in accordance with the general calling hierarchy.
If you do not come up with an efficient tree, your program will
merely be a little unorganized; an incorrect tree will result in
a blink warning message.
--------------------------------------------------------------------
| REMEMBER: Always put your main program into the root node (using |
| either FROM or ROOT). Putting the main program in an overlay |
| node will not work. |
--------------------------------------------------------------------
3. Overlay Node Restrictions
There are restrictions when referencing routines across overlay
nodes as illustrated in Figure 2.
Figure 2: Overlay Tree Example
Root
(a, b, c)
|
--------------------
overlay1 overlay2
(d, e) (f, g)
|
--------------------
overlay3 overlay4
(h) (i)
|
overlay
(j)
-- Any of the overlay routines (d, e, f, g, h, i, j) can
call any of the root node routines (a, b, c).
-- Any of the root node routines (a, b, c) can call
any of the first overlay level routines (d, e, f, g).
-- Routines d and e can call each other and any root node
routines.
-- Routines f and g may call each other, any of the root
node routines, or routines h and i.
-- Routines h may call routines f, g, j, or any of the root
node routines.
-- Routines i may call routines f and g, or any of the root
node routines.
-- Routine j may call routines a, b, c, f, g, and h and only
those routines.
4. WITH Files
Similar to ALINK, blink uses WITH files to inform the linker that
you want overlays--the syntax is the same as for ALINK. After
the keyword OVERLAY, you should list the file or files of your
node or nodes. Use one line per node with as many files as you
want per node. For line continuation, use an asterisk * at the
end of the line which continues on the next line. The nodes
(i.e. file name groups) should be listed in pre-order fashion.
One other factor you should understand about the syntax is
asterisks (*). In addition to acting as a continuation symbol at
the end of a continued WITH line of file overlays, they are also
used at the beginning of a new line IF THE DEPTH OF THE NODE
BEING LISTED IS GREATER THAN A NUMERIC VALUE OF ONE.
As an example, the tree described in Figure 1 would look like this
in WITH file syntax:
OVERLAY overlay1 overlay2 *overlay3 **overlay5 *overlay4
Note the `#'-- this terminates the list as does an End-Of-File.
Also note that since overlays 1 and 2 are at depth 1, they have
no asterisks. The depth of a node is specified by the number of
asterisks. The depth of an overlay in the tree should be equal
to the number of asterisks plus one.
5. The MAP File
When overlays are used, appropriate sections of the map file will
include the information generated by the overlay nodes. Overlay
references and symbols will appear in the map file sections to
which they apply.
The map file sections specific to overlays is obtained using the
O option to the MAP keyword. This generates a section very much
like the hunk section provided by the H option. The O option has
two additional fields which give the overlay level and overlay
ordinate of the overlay node to which the code and data hunks
belong.
If you use the O option but have no overlays, nothing is produced.
___________________________________________________________________
-------------------------------------------------------------------
CUSTOM OVERLAY MANAGERS
You can write your own overlay manager if you need one. A cycle of
compile (or more likely, assemble) accomplishes this task. Include
it with the command:
OVLYMGR file
The object file must contain the symbol _ovlyMgr. This is exactly
how the symbol must appear in the object file; the character case is
important. You may need to omit the leading underscore, since Lattice
C and the assembler both put it in for you. The code hunk must also
have a section name of NTRYHUNK.
Where it is necessary to have a code hunk which absolutely MUST be
the very first hunk in the file, no matter what the linker tries to
do, you should use the section name NTRYHUNK. blink always forces
the section named NTRYHUNK to be the first hunk. However, this hunk
is never merged with any other hunk, and there can only be one such
hunk. Also, use of this section name for your own code segment will
mean that you CANNOT use overlays (unless the segment is your own
overlay manager), but you should try to avoid using this. You do
not have to use OVLYMGR to use the section name NTRYHUNK--just list
it as another object file.
Note that the first hunk is also the program entry point.
For further information about writing your own custom overlay
manager, examine hunk 0 of a program which uses overlays. It
does exactly what is necessary to do the overlays and to handle
the entry point problem.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: 3 ::::: CREATING LOAD-AND-STAY RESIDENT PROGRAMS ::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
This version of the compiler provides an alternative startup module
named CBack.o which allows you to create Load-and-Stay-Resident (LSR)
code. We strongly recommend that you use CBack.o for ANY program
that:
-- has no requirement for the original CLI window for the
purposes of output, OR
-- needs to be memory resident (such as POPCLI or MEMWATCH).
The design specification of CBack.o means that the programmer only
has to make minimal amendments to existing code in order to take
advantage of LSR code.
Linking with CBack.o requires the addition of some definitions to
your main module and an awareness of the setting up which CBack.o
carries out. The first step is the addition of some global
variables necessary for CBack.o to determine how your task will run:
LONG _stack Stack space for the created task
CHAR *_procname Name of the process to be created
LONG _priority The priority to create the process at
LONG _BackGroundIO The flag to indicate an I/O requirement
Most LSR programs will probably only need a stack size of 4000 bytes
(the system default), although this figure of 4000 may need to be
experimented with depending on the LSR application. There are no
restrictions related to the name, although common sense dictates
that a name which is easily recalled is probably best. The task
priority should be set to 0, once more a baseline figure which may
need to be varied. The _BackGroundIO flag is special flag required
when you wish to perform any type of I/O to the originating console.
When this flag is initialized to 0, your code is expected to carry
out its own I/O to a separate window--or no I/O as the case may be.
The latter is also true for any program which opens its own window.
The initializing of _BackGroundIO to a value other than 0 informs
CBack to open the default console and pass it to you in the global
variable _Backstdout. It's the programmer's responsibility to close
this file.
-----------------------------------------------------------------
| WARNING: Failure to close this file means that the original |
| window from which the program was run will NEVER CLOSE. Also, |
| until you close the file handle, the window will remain open. |
-----------------------------------------------------------------
While this may be useful for printing out logon banners, diagnostic
messages or results of commands, a window which cannot be closed
under any circumstances other than a system reboot is indicative of
a low standard of programming.
_Backstdout is an AmigaDOS file handle and consequently all I/O to
this file handle must be performed with the Write() function--NOT
the Lattice functions of write or fprintf. Closure must be only be
done with Close(). Where a program establishes a resident task to
communicate with, the file handle should be passed to this task for
it to print messages on the CLI window.
When the _Backstdout file handle is described as a NULL, your
program cannot perform any output to the window. This can occur when
the program was invoked from the Workbench environment or from
another program which did not have a standard output window available
at the time.
The following example demonstrates a do-nothing program which uses
CBack to install itself:
#include <exec/types.h>
#include <libraries/dos.h>
#include <proto/dos.h>
#include <string.h>
long _stack = 4000; /* a reasonable amount of stack space */
char *_procname = "my_program";
long _priority = 0; /* run at standard priority */
long _BackGroundIO = 1; /* perform background I/O */
extern BPTR _Backstdout; /* file handle we will write to with */
#define MSG "Hello World!"
void main(argc, argv)
int argc;
char *argv[];
{
if (argc == 0) /* called from Workbench, can't do anything */
_exit(0L);
if (_Backstdout) /* called from CLI - print out the string */
{
Write(_Backstdout, MSG, strlen(MSG));
Close(_Backstdout);
}
}
Where an LSR program operates as a single task for the entire system
(such as POPCLI or MEMWATCH, it should check for the existence of a
global message port. If a global message port does not exist, then
the LSR program should create one. After creation of the port, any
control information should be directed through this port. Where a
global message port already exists then this should serve as the
primary port for control information. The sample code for POPCLI
and MEMWATCH demonstrate how this can be achieved.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: 4 ::::::: COMPILER-DEFINED PREPROCESSOR SYMBOLS :::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The Lattice AmigaDOS Compiler by default assigns the following preprocessor
symbols:
M68000 = 1
AMIGA = 1
LATTICE = 1
LATTICE_50 = 1
Using these compiler options will define the following preprocessor
symbols:
Option Preprocessor symbol
-d DEBUG = 1
-w SPTR = 1
w/o -w LPTR = 1
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: 5 ::::::::: DIRECT CALLING OF AMIGA LIBRARIES :::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
In general, the bulk of an Amiga C program will consist of various
library calls. Improvements to this version of the compiler mean that
library routines do not have to go through the stub routines in
amiga.lib, although the option remains for those who wish to still do
this. This gives the advantage of suppressing the code to push the
function parameters on to the stack, calling the stub routine which
places them into the appropriate register and then calls the library.
The compiler can be informed which registers to place the parameters
into and this allows it to select the optimal method of register
loading.
There are two advantages to avoiding the use of the amiga.lib stub
routines:
-- Code execution is faster than code using stub routines.
-- Development time is saved by the avoidance of linking.
The code generated by this technique does not usually show any
significant saving in size over code generated by the conventional
Amiga.Lib route. However, the stub routines can account for a large
proportion of a small program and the execution speed advantage can
become important.
Calling of these direct functions is done through the inclusion of
the prototype header files. These are located on Compiler Disk #2
with the calling syntax of:
#include <proto/lib-name.h>
To use the Open() function of AmigaDOS, the calling sequence would
look like this:
#include <proto/dos.h>
___________________________________________________________________
-------------------------------------------------------------------
CODING LIBRARY CALLS FOR IN-LINE CODE GENERATION
It should be realized that the techniques used for the library
function calls are not hard coded for the known Amiga functions.
This means that these function calls may be freely used in any
source code, although it is necessary to code a statement of the
form:
#pragma libcall <base> <routine> <offset> <magic>
where:
#pragma part of the special calling routine.
libcall part of the special calling routine.
<base> the pointer to the library base.
<routine> the name of the library routine you intend to
directly call.
<offset> the offset from the library base of the routine.
<magic> the description of the parameters.
An example call using the Open() function of AmigaDOS would take
this form:
#pragma libcall DOSBase Open le 2102
where:
DOSBASE the library pointer.
Open the name of the function being called.
le the offset from the library base as a hexadecimal value.
2102 the value used by the compiler to determine the register conventions.
Note that the offsets from the library base actually have negative
values, this is ignored in the interests of clarity.
The calculation of the magic portion is arrived at by breaking down
the number into nibbles representing the register conventions. The
format of the magic number is:
4321r#
where the numbers 4, 3, 2, and 1 indicate the registers where the
fourth, third, second and first parameters respectively, are to be
placed. The values to use for these are:
HEX DATA HEX ADDRESS
VALUE REGISTER VALUE REGISTER
0 D0 8 A0
1 D1 9 A1
2 D2 A A2
3 D3 B A3
4 D4 C A4
5 D5 D A5
6 D6 E A6
7 D7 F A7
Note that address registers A5 to A7 are never used in the above
context. Observant programmers may recognize this pattern as that
assigned by Motorola to the registers in the processor instruction
set.
The `r' portion of the magic number indicates the register where the
result is returned. This is usually 0 for data register D0, although
there are no restrictions as to which register you may select for
return values.
The `#' value indicates the total number of parameters for passing
to the routine. The high-order bit (0X8) of this nibble indicates
that the routine to be called unreasonably destroys the contents of
data registers D6 and D7. In most cases you may ignore this nibble
completely since it is intended to provide a Kickstart version 1.1
compatibility with the Text() and DoIO() functions.
Let's return to our example function call:
#pragma libcall DOSBase Open le 2102
Examining the magic and offset values, you will see how these
figures were arrived at by an examination of vol.2 of the Amiga
ROM Kernel Reference Manual page A-122. The synopsis of the Open()
function is:
file = Open( name, accessMode )
D0 D1 D2
Reference to the previous table will show that data registers D2,
D1 and D0 have the values of 2, 1 and 0 respectively, and this forms
the first part of the value 2102.
If you examine the function OpenLibrary() on page A-124 in vol.2 of
the Amiga ROM Kernel Manual, the synopsis is:
library = OpenLibrary(libname, version)
D0 A1 D0
The first part of our magic value would then be 090, the second part
parameter value is 2, thus the final calculated value is 0902.
The offset values can be obtained from the v1.2 .fd files on the v1.2
Extras disk. Alternatively, the columns starting on page D-5 of vol.1
of the Amiga ROM Kernel Manual provide these. For our example, the
offset value is shown on page D-6 in exec.lib and has a value of 228.
Note that the current edition of vol.1 of the Amiga ROM Kernel Manual
has several errors in the .fd files listing. One of the most readily
identifiable errors shows the incorrect bias for the DOS library --
(add the decimal value 30 or hexadecimal value 1E to correct the
respective columns. There are also several exec.lib functions missing
from the end of the exec.lib listing. A good policy is to always
check the .fd files on your disks rather than rely solely on the
reference manuals.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
: 6 ::::::::::: USING THE FD2PRAGMA PROGRAM :::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The format of the #pragma statements for library calls is relatively
easy to understand and code, it is difficult to maintain and or
decode. We have provided a utility which will take a standard
ASCII .fd file and convert this file to the appropriate #pragma
statements.
The .fd file should consist of single statements per line and
structured in the following form:
-- A line to be processed as a directive starts with two
## (hash) symbols.
-- An asterisk (*) at the start of a line symbolizes a
comment line.
-- Any other line indicates entry points into the library.
The directives for the conversion utility are:
##base _name Identifies the global variable pointing to the
base of the library.
##bias n Identifies the offset value between the library
base and the initial vector in the library.
##public Identifies subsequent lines as being available
to programmers.
##private Identifies subsequent lines as being available to
the library only.
##end Marks the end of the .fd file.
The entry points into the library take the form:
entryname(arguments)(registers)
with commas separating the individual arguments and registers. The
latter may also be separated by a slash character `/'. Some example
entries look like this:
InitCode(startClass,version)(D0/D1)
PrintIText(rp,itext,left,top)(A0/A1,D0/D1)
The first example is from exec.library while the second example is
from intuition.library -- note the mixing of commas and slashes on
the register descriptions.
We strongly suggest that you always maintain and update the .fd
file, running it through the FD2PRAGMA utility rather than trying
amend the #pragmas themselves. This technique is less likely to
lead to errors.
Conversion of the .fd file is done by a command line of the
following syntax:
fd2pragma alpha.fd beta.h
where:
alpha.fd the .fd file for conversion.
beta.h the new header file after conversion.
Any errors encountered in the conversion will be printed on the
standard output file. Typical errors would be invalid directives,
invalid register specifications or no ##end statement.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
: 7 ::::::::::::::::: BUILT-IN FUNCTIONS :::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
This version of the compiler includes several built-in functions
designed to generate high quality 68000 code. These functions are
encouraged by the ANSI standard over others which might have
previously been used. The built-in functions are:
strlen memset
strcmp memcmp
strcpy memcpy
The compiler will automatically recognize a built-in function provided
it is prefixed by the string:
__builtin_
The #defines in the header files string.h and stdlib.h have been
modified to accomplish the invocation of a built-in function. To
take advantage of these functions it is necessary to include the
correct header file -- for these functions it is string.h file.
In situations where you do not want to use the built-in functions,
you can modify the appropriate function with an #undef.
An additional built-in function has been provided to assist in the
speedier development of small programs. This is:
printf
When this function is called, the compiler examines the formatting
string:
-- When it is a constant string with no substitutions, the
compiler changes the printf call to a _write call.
-- When it is a constant string AND only contains %s, %d and
%p formats, a call is made to _tinyprintf.
The purpose behind this is to allow programmers to use printf for
output without having to write a trimmed version which does not
handle the unused formats.
The advantage of built-in functions is related to the ability of the
compiler to exploit register contents. For example, the strlen call
will generate no code if the input string is a constant string. In
addition, functions cannot return the result pointer if it is
unused -- all of this tends to a regime which produces significantly
less code than an equivalent call to the library function.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
: 8 ::::::::::::: HEADER FILE COMPRESSION ::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The utility lcompact can be used to compress header or source files
in a form that can be read much faster by the compiler. The utility
operates by encoding commonly occurring Amiga keywords. Although ANSI
is quite specific about the processing of header or include files we
believe this is a method of increasing the overall compiler
performance without sacrificing ANSI compliance.
To use the utility, you need to feed the source file from stdin and
send the result to stdout. The command line syntax looks like this:
lcompact <source >dest
We have supplied the header files in both compressed and uncompressed
forms. There is no difference in the handling of these -- you do not
have to specify anything special on your command lines or in the
#include statement. You may also have compressed and uncompressed
header files within the same directory.
HEADER FILE ORGANIZATION
There is now a new subdirectory with the header files called proto.
This subdirectory contains a number of header files which contain
function prototypes and the magic material which allow the Amiga
Library functions to operate. If you are calling an Amiga function,
you should include the appropriate header file. However, if you want
to be lazy, you can use the syntax:
#include <proto/all.h>
This will include all the header files although an increase in
compilation time will be the result. Where the system configuration
contains expanded memory and everything is being held in memory,
the increase in compilation time is probably academic.
Note that these files correspond directly to the .fd files since
they were constructed from them.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
: 9 ::::::::::::::Library Documentation Errors::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The following modifications were made to the Libraries and Header files
after the documentation went to print. These changes are designed to
take advantage of the the new ANSI "void *" keyword. By using "void *"
instead of "char *", it will no longer be necessary to put casts in
front of arguments and return values.
Function Page Documentation Correction
=============== ==== ========================
calloc, malloc L19 change "char *b;" to "void *b;"
realloc, free
realloc L19 return value "nb" should be type "void *"
getmem, getml L122 both return a "void *"
erand48,nrand48, L44 The variable seed[3] should be type
jrand48,seed48, "unsigned short", pseed, should be
lcong48 type "unsigned short *", and parm[7]
should be type "unsigned short". This
change is to maintain compatiblity with
System V UNIX.
LCMFFP.LIB G16 The lcmffp.lib DOES contain all of the
floating point functions that are in the
standard Lattice math library.
The sample call to "emit" on page L51 should read
emit(0x4180);
Also, there is a typo on page v of the Preface to the manual. The
reference to the new keyword "_stdards" should read "__stdargs".
Note, all keywords prefaced with a single underscore should be
prefaced with a double underscore.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
: 10 :::::::Additional Notes On The Source Level Debugger::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
IF YOU HAVE ONLY 512K Bytes of MEMORY
READ THIS FIRST BEFORE USING CodeProbe!!!!
CodeProbe requires a large amount of memory to run both itself and an
application. Typically you will want to have at least 300K available
before invoking CPR. There are several things that you may wish to do
to minimize the likelyhood of low memory situations.
1) Invoke CPR with the "-t cpr.tmp" option. CPR creates a temporary file
for quick access to symbolic information that it reads from the
application's executable file. This file can grow to approximately
the same size as the executable file. By default this file is placed
in "ram:". The -t option suggested above will cause the file to be
placed in your current directory. This will be a little slower, however,
it will require less memory.
2) Invoke CPR with the "-w" option. This option will tell CPR to open its
windows on the WorkBench Screen rather than on its own custom screen.
This will also save memory.
For example to invoke the ftoc example program under limited memory
conditions, a user might type:
cpr -w -t cpr.tmp ftoc
Several additions were made to CodeProbe after the manuals went to
print.
The File Menu has an additional Item called "Find Current Line".
This option will automatically cause the source window to find the line
of program execution. Both the "Find Current Line" and "Line" options
will now appear as "Find Current Address" and "Address" when the debugger
is in Assembler mode. When prompted for an address, you should enter a
hexadecimal value. A '0x' prefix is not required. The Source window
will disassemble code beginning at the specified address.
The Options menu also has an addition item called "Step Into ResLib".
This option allows the user to decide whether or not to allow CPR to step
into resident libraries while tracing or running an application with
Watch Breaks. This option defaults to "off". It should never be turned
on unless you wish to debug your own resident libraries. Tracing into
system libraries such as Exec or Intuition can lead to problems. As
long as this option remains off, it is safe to run with Watch Breaks on
while stepping over system resident libraries.
The mouse can be used to perform a "go line" or "go address" command
by double clicking the left mouse button with either shift key pressed.
This compliments the default double click action of "break line" or
"break address".
There are several Documentation Errors:
There is reference to a -n option on page D130. No such option has
been implemented in this release. If you wish to catch a task started
from outside of the debugger, you will have to bring up some other
program. You will not of course have to run it.
The information about the Symload command on pages D130 and D131 is
incorrect. This command can take several forms:
symload proc "process name"
will find the executable file and the loaded memory segments associated
with the specified process if it has CLI structure. (Note that "symload"
and "proc" are required keywords in the above example). If the command has
an executable file name as an additional argument, any debug information
associated with that file will be loaded instead of looking for the file
associated with the process. The address of a process task block can be
specified in place of the process name.
Symload can also be given a pointer to a segment list as an argument:
symload seg 0x123456 c:executable
Note that this form requires the keywords "symload" and "seg" as well as
a the hexadecimal address pointing to the process seglist. The name
of an executable file is also required.
An ARexx interface is supported by CPR. Macros with a ".cpr" extension
are recognized. CPR commands return status codes to ARexx as follows:
0 The debugger command was successful.
1 The command was syntactically correct, but failed for some
other reason.
2 A syntax error occurred.
3 The debugger could not allocate memory for the ARexx
message.
In addition to the above error codes, an ARexx macro can receive the
output of a debugger command as it would normally appear in the Dialog
Window by invoking the ARexx command "options results". The output
will then be accessable from the variable "result", provided that the
debugger returned a 0 return code.
A number of useful macros are provided on disk 4 in the ":examples/debugger"
directory. These examples simulate "wack" commands. For additional
information on these macros, read the file CprARexx.DOC on disk 4. If you
have any useful ARexx macros you would like to share with other Lattice users
please upload them to the Lattice Bulletin Board Service or the BIX network.
We hope that you find release 1.0 of our Source Level Debugger beneficial
for your development needs. We have incorporated many suggestions from our
users into the current release, ARexx support is one example. We will
continue to make enhancements to the product, and we hope that you will
continue to help shape the future of this tool by offering comments and
suggestions. We look forward to hearing from you.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
: 11 ::::::::::::::::::New Keywords in 5.0:::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The new compiler introduces several new keywords:
The simplest of them is the keyword 'signed'. It works exactly
like the unsigned keyword to ensure that a particular variable
is not unsigned. While this is the default, the -cu option will
cause characters to be treated as 'unsigned'.
example:
signed char c;
extern signed int x;
The 5.0 compiler provides several keywords to indicate the
storage class of an object. With the 4.0 compiler, the only
way to change the storage class was to use the -b0 or -b1
flag in conjunction with the -a option on LC2. These options
are still available, but the recommended method is to let the
compiler default to near addressing (-b1) and then use the
keywords where necessary. Unlike MS-DOS based compilers, these
keywords do not affect the size of an object, but instead
indicate the storage class. In that vein, you MUST place the
keyword as close to the data item as possible:
extern int __near x; /* addressed as a4 relative */
extern long __far y; /* addressed with a 32 bit absolute */
extern struct Custom far custom; /* "" */
extern char * near y;
static int far y; /* Place in the far section */
Note that you can not use the storage keyword except immediately
before the target object.
extern far int wrong; /* <<< Illegal */
static near char *nogood; /* <<< Not allowed */
The most important of these new storage class keywords is the
chip (or __chip) keyword.
static struct Image chip myimage = { ... };
extern struct Image chip pict;
This will cause the compiler to place the target object into memory
addressable by the Amiga custom chips.
It is also important to note that that storage class keywords allow
two versions: __far and far. The version with the double underscores
is intended to allow for an ANSI conforming program while we offer
the non-ANSI version for more readable code and some compatibility
with MS-DOS based products.
The 5.0 compiler also provides a number of keywords that may be
applied to functions to permit special calling conventions.
Of importance are the __regargs and __stdargs keywords which indicate
that the compiler is to use an altered calling convention. By
default, the compiler will use __regargs for everything. However,
if you use the -rr option of LC1, the compiler will use a __regargs
convention in which the first two data items and first two
pointer items are passed in d0/d1 and a0/a1 respectively. The keywords
allow you to override the default. For example:
extern int __stdargs myprintf();
long __regargs foo(int i) { ... }
__stdargs void bar();
These keywords may appear anywhere before the identifier to which they
are applied.
With the 4.0 compiler, it was necessary to use the -y option on LC2
in order to load up the address of the global data base at entry to
a function. This however applied to all functions in a module.
To cause it to be loaded for a single function, you can use the
keyword __saveds as in:
int __saveds myentry() { .... }
Note that __saveds only has meaning when applied to the actual
definition of the function. External functions with the __saveds
keyword simply ignore the keyword.
Like the __saveds keyword, the __interrupt keyword is applied to
a function to indicate that this function may be called from an
interrupt routine. Although at this release it currently does not
affect the code generated for the function, it is provided for
potential variations in code necessary to support interrupts on
non-Amiga compilers.
Providing the most control over register passing, the __asm keyword
allows you to specify exactly which registers parameters are to
be passed in. It can be used for both function definitions and
function declarations:
extern int __asm mymax( register __d0 int i, register __d1 int j);
int __asm myfun( i,p )
register __d0 int i;
register __a1 char *p;
In order for the register specifier sequence to be used, you must
have the __asm keyword specified on the function. If you do use
the __asm keyword, you MUST specify a register for all parameters
and not reuse the same register for any two parameters. If you
need to pass some parameters on the stack then you should consider
using the __regargs keyword instead.
In order to permit the most flexibility in register passing, the
compiler does not limit what registers may be passed. However this
can lead to some situations which would make it impossible to
generate code that works in the presence of aliased variables.
To ensure that such situations are not presented, you should avoid
utilizing registers that would normally assigned as register variables
and instead stick to the registers:
__d0 __d1 __d2 __d3 __a0 __a1 __a6
The best advice is to be careful when using this feature and if you
are uncomfortable with it, utilize the -rr option of LC1 (or
__regargs) in which the compiler will pick the best registers.
The keywords __asm, __stdargs, and __regargs are mutually exclusive.
*** Please note, the double underscores ***
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
: 12 :::::::::::::::::::Library Descriptions:::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
cback.o
Startup routine to detach process from CLI and run in background.
catchres.o
Startup routine to catch software exceptions in resident programs.
lcs.lib
Lattice C Library for use with 16-bit integers.
catch.o
Startup routine to catch software exceptions.
lcms.lib
Lattice Standard IEEE Math Library for use with 16-bit integers.
lcm881.lib
Lattice 68881 Coprocessor Math Library.
c.o
Standard Lattice Startup routine.
lcnb.lib
Lattice C Library for use with no base-relative data addressing.
catchresnr.o
Startup - Catch Exceptions, Resident, No Requesters on exception.
lcmffp.lib
Lattice Motorola Fast Floating Point Math Library.
lcmieee.lib
Lattice IEEE Math Library for use with Commodore Resident Library.
lcsr.lib
Lattice C Library for use with 16-bit integers and Registerized Parameters.
lcr.lib
Lattice C Library for use with Registerized Parameters.
lcmr.lib
Lattice IEEE Math Library for use with Registerized Parameters.
lc.lib
Lattice Standard C Library.
cres.o
Startup routine for Resident Programs.
lcm.lib
Lattice Standard IEEE Math Library.
lcsnb.lib
Lattice C Library for 16-bit integers and no base-relative
addressing.
ddebug.lib
Commodore debug library for use with Parallel Port.
debug.lib
Commodore debug library for use with Serial Port.
amiga.lib
Library of linkage routines to Amiga Resident Libraries.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
: 13 ::::::::::::::::::Binary Patch Program::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Lattice traditionally releases software "patches" to correct problems
which are found. Included on Disk 3 is a binary patch utility named
"PATCH" which may be used to apply future patches (provided by Lattice)
to the compiler and tools. The calling sequence is as follows:
PATCH oldfile [-ooutput -ppatchfile]
where:
oldfile is the old file to be patched.
output is the output file, default oldfile.new
patchfile is the patchfile supplied by Lattice, Inc.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
: 14 :::::::::::::::::::::: 68881 Notes :::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
M68881
The code generated for the 68881 (-f8 option) runs differently than
than standard code in the following ways:
1) Converting doubles to longs does not truncate the decimal
places as with standard code. Instead it rounds off.
2) The recommended procedure for calling trig functions is to
use the header file M68881.h. This code will run much faster
than the routines in the lcm881.lib library.
3) The trig functions do not return the proper value if a
error occurs, ie acos(1.1).
4) There is a known bug in placing a constant into a single
precision float in modules compiled with -f8.
:::::::::::::::::::END OF FILE:::::::::::::::::::::::::::::::::::::::::::